package top.codecrab.eblog.common.interceptor;

import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import top.codecrab.eblog.common.annotation.AccessLimit;
import top.codecrab.eblog.common.response.R;
import top.codecrab.eblog.config.Constants;
import top.codecrab.eblog.utils.CommonUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 解决方案博主：https://onestar.newstar.net.cn/blog/73
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SessionInterceptor implements HandlerInterceptor {

    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 跨域时会首先发送一个OPTIONS请求，这里我们给OPTIONS请求直接返回正常状态
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setStatus(org.springframework.http.HttpStatus.OK.value());
            return true;
        }

        // 判断请求是否属于方法的请求
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod hm = (HandlerMethod) handler;
        // 获取方法中的注解,看是否有该注解
        AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
        if (null == accessLimit) {
            return true;
        }

        String ip = CommonUtils.getRemoteHost(request);
        String servletPath = request.getServletPath();
        String key = Constants.GLOBAL_REDIS_PREFIX + "visitCount:" + servletPath + ":" + ip;

        BoundValueOperations<String, String> valueOps = stringRedisTemplate.boundValueOps(key);
        String visitCountStr = valueOps.get();
        if (visitCountStr == null) {
            return true;
        }

        int visitCount = Integer.parseInt(visitCountStr);
        // 如果值小于等于允许请求次数，就返回true
        if (visitCount <= accessLimit.maxCount()) {
            log.info("{} 未超出，访问次数 =======> {}", servletPath, visitCount);
            return true;
        }

        // 以下只剩下超出访问次数，继续记录
        log.info("{} 超出了，访问次数 =======> {}", servletPath, visitCount);
        //返回 json 请求过于频繁请稍后再试
        String jsonStr = JSONUtil.toJsonStr(new R(-1, "请求过于频繁请稍后再试", null));
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().print(jsonStr);
        } catch (IOException e) {
            log.error("{} 返回“请求过于频繁请稍后再试时”出错 ====> {}", servletPath, e.getMessage());
        }
        return false;
    }

}
